import React, {useEffect, useRef, useState, useMemo} from 'react'
import {init, dispose, registerIndicator, Chart, registerOverlay} from 'klinecharts'
import Datasource from '../data/datasource'
import generatedDataList from '../generatedDataList'
import Layout from '../Layout'
import axios from 'axios'
import Style from './Style'
import KD from './indicators/KDJ'
import BOLLMA, { calculateBollSeries } from "./indicators/BOLLMA";
import BOLLDIV from "./indicators/BOLLDIV";
import HIGH_SLOPE from "./indicators/HIGH_SLOPE";
import SMART_BOTTOM from "./indicators/SMART_BOTTOM";
import StrategiesCollection from "../strategies";
import ANGLE from "./indicators/ANGLE";
import moment from 'moment'
import _ from 'lodash'
import stocksData from '../data'
import tradingDates from '../data/dates'
import datasource from "../data/datasource";
import Overlays from "./overlays";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faMagnifyingGlass, faXmark} from "@fortawesome/free-solid-svg-icons";
import { io } from "socket.io-client";
import BUGKDJ from "./indicators/BUGKDJ";
import BOLLBREAKOUT from "./indicators/BOLL_BREAKOUT";
import ZLEMA from "./indicators/ZLEMA";
import ZIGLAG, { calculateMT4ZigZag } from "./indicators/ZIGLAG";
import FlatBoll, { calculateFlatBoll } from "./indicators/FlatBoll";
import EquityChart from '../strategies/components/EquityChart'
import QuickHull from '../algorithms/quickHull'
const socket = io("wss://stock.deployment.cn", {
    reconnectionDelayMax: 10000
});

const Strategies = StrategiesCollection.strategies
const indicatorStrategies = Strategies.filter(strategy => strategy.indicator)
const userStrategies = Strategies.filter(strategy => !strategy.indicator)
const Trader = StrategiesCollection.Trader
var trader;

const mainIndicators = ['MA', 'EMA', 'BOLL']
const subIndicators = ['KDJ', 'RSI', 'VOL']
let defaultFavs = [
    { code: "000001.SH", name: "上证指数" },
    // { code: "399001.SZ", name: "深证成指" },
    { code: "399006.SZ", name: "创业板指" },
    // { code: "000300.SH", name: "沪深300" },
    // { code: "000016.SH", name: "上证50" },
    // { code: "000905.SH", name: "中证500" }
]
let defaultStrategy = Strategies[0]
if(typeof window !== 'undefined' && localStorage.getItem("strategy")) {
    const savedStrategyName = localStorage.getItem("strategy")
    const foundStrategy = Strategies.find(strategy => strategy.name === savedStrategyName)
    if (foundStrategy) {
        defaultStrategy = foundStrategy
    } else {
        // 如果保存的策略不存在，清除并使用默认值
        localStorage.setItem("strategy", defaultStrategy.name)
    }
} else if (typeof window !== 'undefined') {
    // 如果没有保存的策略，保存默认策略
    localStorage.setItem("strategy", defaultStrategy.name)
}
var dFavs = [...defaultFavs]
if(localStorage.getItem("favs")) {
    dFavs = JSON.parse(localStorage.getItem("favs"))
}
Overlays.forEach(overlay => {
    registerOverlay(overlay)
})
let lastLines = [];
const EXTRA_INDICATORS_STORAGE_KEY = 'extraIndicators'
const EXTRA_INDICATORS_VISIBILITY_STORAGE_KEY = 'extraIndicatorsVisibility' // 控制 toggle 按钮是否显示
const DEFAULT_EXTRA_INDICATORS = {
    breakout55: true,
    zlema: false,
    trendChange: false,
    hull: true,  // 默认启用 hull
    ziggLag: false,  // 默认不启用 ZigLag
    flatBoll: false,  // 默认不启用 FlatBoll
    predict: false  // 默认不启用 Predict
}
const DEFAULT_EXTRA_INDICATORS_VISIBILITY = {
    breakout55: true,
    zlema: true,
    trendChange: true,
    hull: true,  // toggle 按钮默认显示
    ziggLag: true,  // toggle 按钮默认显示
    flatBoll: true,  // toggle 按钮默认显示
    predict: true  // toggle 按钮默认显示
}
const EXTRA_INDICATOR_DEFAULT_PARAMS = {
    breakout55: {
        length: 55,
        multiplier: 1
    },
    zlema: {
        length: 70,
        multiplier: 1.2
    },
    trendChange: {
        swingLen: 2,
        boxPct: 18.0,
        minBoxBars: 3
    },
    hull: {
        lineWidth: 3  // 默认线宽
    },
    ziggLag: {
        depth: 12,
        deviation: 5,
        backstep: 2,
        lineWidth: 2,
        lineUpColor: '#00ff00', // lime
        lineDnColor: '#ff0000'  // red
    },
    flatBoll: {
        bbLength: 20,
        bbMult: 2.0,
        thresholdType: 'Ticks',  // 'Ticks' or '%'
        thresholdWidth: 0.055,
        thresholdWidthPercent: 1.0,
        addATRFilter: false,
        atrLength: 14,
        atrSmoothing: 'RMA',  // 'RMA', 'SMA', 'EMA', 'WMA'
        atrThreshold: 25
    },
    predict: {
        predLen: 10,  // 预测长度
        T: 0.9,  // 温度参数（推荐范围：0.8-1.2，较低值更保守，较高值更随机）
        topP: 0.9,  // Top-p 采样参数（推荐范围：0.8-0.95，0.9 是平衡值）
        sampleCount: 3,  // 采样次数（推荐范围：1-5，多次采样取平均更稳定但更慢）
        apiBaseUrl: 'http://192.168.2.65:5000'  // API 基础地址
    }
}
const EXTRA_INDICATOR_OPTIONS = [
    {
        key: 'breakout55',
        label: 'BB Breakout',
        params: [
            { key: 'length', label: '长度', min: 10, max: 120, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'multiplier', label: '倍数', min: 1, max: 3, step: 0.1, decimals: 1, color: '#ffb74d' }
        ]
    },
    {
        key: 'zlema',
        label: 'ZLEMA',
        params: [
            { key: 'length', label: '长度', min: 70, max: 200, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'multiplier', label: '倍数', min: 0.5, max: 3, step: 0.1, decimals: 1, color: '#ffb74d' }
        ]
    },
    {
        key: 'trendChange',
        label: '趋势变化',
        params: [
            { key: 'swingLen', label: 'Swing长度', min: 1, max: 10, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'boxPct', label: '宽度(%)', min: 0.1, max: 50, step: 0.1, decimals: 1, color: '#ffb74d' },
            { key: 'minBoxBars', label: '最小K线数', min: 1, max: 20, step: 1, decimals: 0, color: '#0dd697' }
        ]
    },
    {
        key: 'hull',
        label: 'HULL',
        params: [
            { key: 'lineWidth', label: '线宽', min: 0.1, max: 3, step: 0.1, decimals: 1, color: '#0dd697' }
        ]
    },
    {
        key: 'ziggLag',
        label: 'ZigLag',
        params: [
            { key: 'depth', label: '深度', min: 1, max: 100, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'deviation', label: '偏差%', min: 1, max: 100, step: 1, decimals: 0, color: '#ffb74d' },
            { key: 'backstep', label: '回退', min: 2, max: 50, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'lineWidth', label: '线宽', min: 1, max: 4, step: 1, decimals: 0, color: '#0dd697' }
        ]
    },
    {
        key: 'flatBoll',
        label: 'FlatBoll',
        params: [
            { key: 'bbLength', label: 'BB长度', min: 5, max: 100, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'bbMult', label: '标准差倍数', min: 0.5, max: 5, step: 0.1, decimals: 1, color: '#ffb74d' },
            { key: 'thresholdWidth', label: '阈值宽度', min: 0.001, max: 1, step: 0.001, decimals: 3, color: '#0dd697' },
            { key: 'thresholdWidthPercent', label: '阈值宽度%', min: 0.1, max: 10, step: 0.1, decimals: 1, color: '#ffb74d' }
        ]
    },
    {
        key: 'predict',
        label: 'Predict',
        params: [
            { key: 'predLen', label: '预测长度', min: 5, max: 120, step: 1, decimals: 0, color: '#0dd697' },
            { key: 'T', label: '温度', min: 0.1, max: 2.0, step: 0.1, decimals: 1, color: '#ffb74d' },
            { key: 'topP', label: 'Top-p', min: 0.1, max: 1.0, step: 0.05, decimals: 2, color: '#0dd697' },
            { key: 'sampleCount', label: '采样次数', min: 1, max: 10, step: 1, decimals: 0, color: '#ffb74d' }
        ]
    }
]
const STRATEGY_VISIBILITY_STORAGE_KEY = 'strategyVisibility'
const DEFAULT_STRATEGY_VISIBILITY = userStrategies.reduce((acc, strategy) => {
    if (strategy && strategy.name) {
        acc[strategy.name] = strategy.name === '游戏' ? true : true
    }
    return acc
}, {})

export default function KLineChart () {
    const chart = useRef()
    const paneId = useRef('')
    const [stockName, setStockName] = useState("上证指数")
    const [code, setCode] = useState("000001.SH")
    const [prices, setPrices] = useState([])
    const [strategy, setStrategy] = useState(defaultStrategy)
    const [strategyStep, setStrategyStep] = useState(0)
    const [favs, setFavs] = useState(dFavs)
    const [period, setPeriod] = useState('1d')
    const [endTime, setEndTime] = useState(moment().format("YYYYMMDD150000"))
    const [searchedStocks, setSearchedStocks] = useState([])
    const [search, setSearch] = useState("")
    const [lines, setLines] = useState(300)
    const [price, setPrice] = useState(0)
    const [cash, setCash] = useState(0)
    const [position, setPosition] = useState(0)
    const [infoDisplay, setInfoDisplay] = useState(true)
    const [sourceReady, setSourceReady] = useState(false)
    const [strategyInfoShow, setStrategyInfoShow] = useState(true)
    const [menuShow, setMenuShow] = useState(false)
    const [selectedLine, setSelectedLine] = useState(null)
    const [profit, setProfit] = useState(0); // 新增：累计收益
    const [cumulativeReturn, setCumulativeReturn] = useState(0); // 新增：累积收益率
    const [extraIndicators, setExtraIndicators] = useState(DEFAULT_EXTRA_INDICATORS)
    const [extraIndicatorParams, setExtraIndicatorParams] = useState(EXTRA_INDICATOR_DEFAULT_PARAMS)
    const [extraIndicatorsVisibility, setExtraIndicatorsVisibility] = useState(DEFAULT_EXTRA_INDICATORS_VISIBILITY) // 控制 toggle 按钮是否显示
    const zlemaIndicatorIdRef = useRef(null) // ZLEMA 指标的 ID（主图用）
    const flatBollIndicatorIdRef = useRef(null) // FlatBoll 指标的 ID（主图用）
    const flatBollOverlaysRef = useRef([]) // FlatBoll overlay 引用（用于填充区域）
    const [strategyVisibility, setStrategyVisibility] = useState(DEFAULT_STRATEGY_VISIBILITY)
    const [settingsVisible, setSettingsVisible] = useState(false)
    const [equityHistory, setEquityHistory] = useState([]) // 收益率曲线数据
    const trendChangeOverlaysRef = useRef([]) // 趋势变化 overlay 引用
    const hullOverlaysRef = useRef([]) // HULL overlay 引用
    const hullModeRef = useRef(1) // HULL 模式状态（1=high, 2=low）
    const hullLastHighLineRef = useRef([]) // 存储最后的高点线
    const hullLastLowLineRef = useRef([]) // 存储最后的低点线
    const hullStopIndexRef = useRef(0) // 存储停止索引
    const breakoutTopOverlay = useRef(null)
    // ZigLag overlay 引用
    const ziggLagOverlaysRef = useRef([]) // ZigLag overlay 引用
    const ziggLagLastDirectionRef = useRef(0) // 最后一个方向
    const ziggLagLastZ1Ref = useRef(null) // 最后一个 z1 点
    const ziggLagLastZ2Ref = useRef(null) // 最后一个 z2 点
    
    // Predict 子图相关
    const predictChartRef = useRef(null) // Predict 子图引用
    const predictOverlayRef = useRef(null) // Predict overlay 引用（红色半透明覆盖）
    const [predictData, setPredictData] = useState([]) // 预测数据
    const [isPredicting, setIsPredicting] = useState(false) // 是否正在预测
    useEffect(() => {
        setEquityHistory([])
    }, [strategy?.name])
    const breakoutBottomOverlay = useRef(null)
    useEffect(() => {
        try {
            const storedIndicators = localStorage.getItem(EXTRA_INDICATORS_STORAGE_KEY)
            if (storedIndicators) {
                const parsedIndicators = JSON.parse(storedIndicators)
                if (parsedIndicators && typeof parsedIndicators === 'object') {
                    setExtraIndicators(prev => ({
                        ...prev,
                        ...parsedIndicators
                    }))
                }
            }
        } catch (e) {
            console.warn('failed to load extra indicators from localStorage', e)
        }
        try {
            const storedParams = localStorage.getItem(`${EXTRA_INDICATORS_STORAGE_KEY}_params`)
            if (storedParams) {
                const parsedParams = JSON.parse(storedParams)
                if (parsedParams && typeof parsedParams === 'object') {
                    setExtraIndicatorParams(prev => ({
                        ...prev,
                        ...parsedParams
                    }))
                }
            }
        } catch (e) {
            console.warn('failed to load extra indicator params from localStorage', e)
        }
        try {
            const storedVisibility = localStorage.getItem(EXTRA_INDICATORS_VISIBILITY_STORAGE_KEY)
            if (storedVisibility) {
                const parsedVisibility = JSON.parse(storedVisibility)
                if (parsedVisibility && typeof parsedVisibility === 'object') {
                    setExtraIndicatorsVisibility(prev => ({
                        ...prev,
                        ...parsedVisibility
                    }))
                }
            }
        } catch (e) {
            console.warn('failed to load extra indicators visibility from localStorage', e)
        }
    }, [])
    useEffect(() => {
        try {
            localStorage.setItem(EXTRA_INDICATORS_STORAGE_KEY, JSON.stringify(extraIndicators))
        } catch (e) {
            console.warn('failed to store extra indicators to localStorage', e)
        }
    }, [extraIndicators])
    useEffect(() => {
        try {
            localStorage.setItem(EXTRA_INDICATORS_VISIBILITY_STORAGE_KEY, JSON.stringify(extraIndicatorsVisibility))
        } catch (e) {
            console.warn('failed to store extra indicators visibility to localStorage', e)
        }
    }, [extraIndicatorsVisibility])
    useEffect(() => {
        try {
            localStorage.setItem(`${EXTRA_INDICATORS_STORAGE_KEY}_params`, JSON.stringify(extraIndicatorParams))
        } catch (e) {
            console.warn('failed to store extra indicator params', e)
        }
    }, [extraIndicatorParams])
    useEffect(() => {
        try {
            const storedVisibility = localStorage.getItem(STRATEGY_VISIBILITY_STORAGE_KEY)
            if (storedVisibility) {
                const parsed = JSON.parse(storedVisibility)
                if (parsed && typeof parsed === 'object') {
                    setStrategyVisibility(prev => ({
                        ...prev,
                        ...parsed,
                        游戏: true
                    }))
                }
            }
        } catch (e) {
            console.warn('failed to load strategy visibility', e)
        }
    }, [])
    useEffect(() => {
        try {
            const toStore = { ...strategyVisibility, 游戏: true }
            localStorage.setItem(STRATEGY_VISIBILITY_STORAGE_KEY, JSON.stringify(toStore))
        } catch (e) {
            console.warn('failed to store strategy visibility', e)
        }
    }, [strategyVisibility])

    const removeBreakoutOverlays = () => {
        if (!chart.current) {
            breakoutTopOverlay.current = null
            breakoutBottomOverlay.current = null
            return
        }
        if (breakoutTopOverlay.current) {
            try {
                chart.current.removeOverlay(breakoutTopOverlay.current)
            } catch (e) {
                // ignore removal errors
            }
            breakoutTopOverlay.current = null
        }
        if (breakoutBottomOverlay.current) {
            try {
                chart.current.removeOverlay(breakoutBottomOverlay.current)
            } catch (e) {
                // ignore removal errors
            }
            breakoutBottomOverlay.current = null
        }
    }

    const createBandOverlay = (chartInstance, points, color) => {
        if (!chartInstance || !points || points.length < 3) {
            return null
        }
        return chartInstance.createOverlay({
            name: 'breakoutBand',
            points,
            extendData: { color }
        }, 'candle_pane')
    }

    const updateBreakoutOverlays = (chartInstance, dataList) => {
        if (!extraIndicators.breakout55) {
            removeBreakoutOverlays()
            return
        }
        removeBreakoutOverlays()
        if (!chartInstance || !dataList || dataList.length === 0) {
            return
        }
        const breakoutConfig = extraIndicatorParams.breakout55 ?? EXTRA_INDICATOR_DEFAULT_PARAMS.breakout55
        const breakoutSeries = calculateBollSeries(dataList, [breakoutConfig.length ?? 55, breakoutConfig.multiplier ?? 1])
        const topPoints = []
        const midPoints = []
        const bottomPoints = []
        breakoutSeries.forEach((seriesItem, index) => {
            if (!seriesItem) {
                return
            }
            const data = dataList[index]
            const timestamp = data?.timestamp ?? data?.time
            if (timestamp == null) {
                return
            }
            if (seriesItem.up != null) {
                topPoints.push({ timestamp, value: seriesItem.up })
            }
            if (seriesItem.mid != null) {
                midPoints.push({ timestamp, value: seriesItem.mid })
            }
            if (seriesItem.dn != null) {
                bottomPoints.push({ timestamp, value: seriesItem.dn })
            }
        })
        if (topPoints.length < 2 || midPoints.length < 2 || bottomPoints.length < 2) {
            return
        }
        const topPolygon = [...midPoints, ...topPoints.slice().reverse()]
        const bottomPolygon = [...bottomPoints, ...midPoints.slice().reverse()]

        breakoutTopOverlay.current = createBandOverlay(chartInstance, topPolygon, 'rgba(0, 220, 170, 0.32)')
        breakoutBottomOverlay.current = createBandOverlay(chartInstance, bottomPolygon, 'rgba(255, 170, 20, 0.28)')
    }

    const handleExternalDataApplied = async (dataList) => {
        if (!Array.isArray(dataList) || dataList.length === 0) {
            setPrices([])
            removeBreakoutOverlays()
            // 清除趋势变化 overlay
            trendChangeOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            trendChangeOverlaysRef.current = []
            // 清除 HULL overlay
            hullOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            hullOverlaysRef.current = []
            hullModeRef.current = 1
            hullLastHighLineRef.current = []
            hullLastLowLineRef.current = []
            hullStopIndexRef.current = 0
            // 清除 ZigLag overlay
            ziggLagOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            ziggLagOverlaysRef.current = []
            ziggLagLastDirectionRef.current = 0
            ziggLagLastZ1Ref.current = null
            ziggLagLastZ2Ref.current = null
            // 清除 Predict 数据
            setPredictData([])
            return
        }
        const cloned = dataList.slice()
        setPrices(cloned)
        // 只在非回测模式时绘制首页指标
        if (infoDisplay) {
            updateBreakoutOverlays(chart.current, cloned)
            if (extraIndicators.trendChange) {
                drawTrendChangeOverlays(cloned)
            }
            if (extraIndicators.hull) {
                const currentLineWidth = extraIndicatorParams.hull?.lineWidth ?? EXTRA_INDICATOR_DEFAULT_PARAMS.hull?.lineWidth ?? 3
                drawHullOverlays(cloned, true, currentLineWidth)  // 数据重新加载时重置状态
            }
            if (extraIndicators.ziggLag) {
                drawZiggLagOverlays(cloned, true)  // 数据重新加载时重置状态
            }
            if (extraIndicators.flatBoll) {
                drawFlatBollFillOverlays(cloned)  // 数据重新加载时绘制填充区域
            }
        }
        // Predict 子图始终更新（包括回测模式）
        if (extraIndicators.predict) {
            await updatePredictChart(cloned)
        }
    }
    useEffect(() => {
        if (extraIndicators.breakout55) {
            updateBreakoutOverlays(chart.current, prices)
        } else {
            removeBreakoutOverlays()
        }
    }, [extraIndicators.breakout55, extraIndicatorParams.breakout55?.length, extraIndicatorParams.breakout55?.multiplier, prices])
    
    // 趋势变化指标控制（只在非回测模式时绘制）
    useEffect(() => {
        // 如果 infoDisplay 为 false，说明在回测模式，不绘制首页指标（策略会自己绘制）
        if (extraIndicators.trendChange && prices && prices.length > 0 && infoDisplay) {
            drawTrendChangeOverlays(prices)
        } else {
            // 清除 overlay
            trendChangeOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            trendChangeOverlaysRef.current = []
        }
    }, [extraIndicators.trendChange, extraIndicatorParams.trendChange?.swingLen, extraIndicatorParams.trendChange?.boxPct, extraIndicatorParams.trendChange?.minBoxBars, prices, infoDisplay])
    
    // HULL 指标控制（只在非回测模式时绘制）
    useEffect(() => {
        // 如果 infoDisplay 为 false，说明在回测模式，不绘制首页指标
        if (extraIndicators.hull && prices && prices.length > 0 && infoDisplay) {
            // 参数变化时只重新绘制，不重置状态，并传入最新参数确保使用最新值
            const currentLineWidth = extraIndicatorParams.hull?.lineWidth ?? EXTRA_INDICATOR_DEFAULT_PARAMS.hull?.lineWidth ?? 3
            drawHullOverlays(prices, false, currentLineWidth)
        } else {
            // 清除 overlay
            hullOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            hullOverlaysRef.current = []
            // 重置状态
            hullModeRef.current = 1
            hullLastHighLineRef.current = []
            hullLastLowLineRef.current = []
            hullStopIndexRef.current = 0
        }
    }, [extraIndicators.hull, extraIndicatorParams.hull?.lineWidth, prices, infoDisplay])
    
    // ZigLag 绘制函数（基于 MT4 ZigZag 算法）
    const drawZiggLagOverlays = (dataList, shouldResetState = false) => {
        if (!chart.current || !dataList || dataList.length < 2) {
            return
        }
        
        const params = extraIndicatorParams.ziggLag ?? EXTRA_INDICATOR_DEFAULT_PARAMS.ziggLag
        const depth = params?.depth ?? 12
        const deviation = params?.deviation ?? 5
        const backstep = params?.backstep ?? 2
        const lineWidth = params?.lineWidth ?? 2
        const lineUpColor = params?.lineUpColor ?? '#00ff00'
        const lineDnColor = params?.lineDnColor ?? '#ff0000'
        
        // 清除之前的 overlay
        const oldOverlayIds = [...ziggLagOverlaysRef.current]
        ziggLagOverlaysRef.current = []
        oldOverlayIds.forEach(id => {
            try {
                chart.current.removeOverlay(id)
            } catch (e) {
                // ignore
            }
        })
        
        // 重置状态
        if (shouldResetState) {
            ziggLagLastDirectionRef.current = 0
            ziggLagLastZ1Ref.current = null
            ziggLagLastZ2Ref.current = null
        }
        
        // 计算完整的 ZigZag
        const result = calculateMT4ZigZag(dataList, depth, deviation, backstep)
        
        if (!result || !result.points || result.points.length < 2) {
            return
        }
        
        // 绘制所有连接摆动点的线段
        const points = result.points
        for (let i = 0; i < points.length - 1; i++) {
            const point1 = points[i]
            const point2 = points[i + 1]
            
            const candle1 = dataList[point1.barIndex]
            const candle2 = dataList[point2.barIndex]
            
            if (!candle1 || !candle2) continue
            
            // 根据方向确定颜色
            const isUpward = point2.type === 'high' && point1.type === 'low'
            const color = isUpward ? lineUpColor : lineDnColor
            
            const lineId = chart.current.createOverlay({
                name: 'segment',
                points: [
                    { timestamp: candle1.timestamp, value: point1.price },
                    { timestamp: candle2.timestamp, value: point2.price }
                ],
                styles: {
                    line: {
                        color: color,
                        size: lineWidth,
                        style: 'solid'
                    }
                }
            })
            
            if (lineId) {
                ziggLagOverlaysRef.current.push(lineId)
            }
        }
        
        // 保存最后的状态
        if (points.length >= 2) {
            const z1 = points[points.length - 2]
            const z2 = points[points.length - 1]
            ziggLagLastDirectionRef.current = result.direction
            ziggLagLastZ1Ref.current = { barIndex: z1.barIndex, price: z1.price }
            ziggLagLastZ2Ref.current = { barIndex: z2.barIndex, price: z2.price }
        }
    }
    
    // ZigLag 指标控制（只在非回测模式时绘制）
    useEffect(() => {
        if (extraIndicators.ziggLag && prices && prices.length > 0 && infoDisplay) {
            drawZiggLagOverlays(prices, false)
        } else {
            // 清除 overlay
            ziggLagOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            ziggLagOverlaysRef.current = []
            // 重置状态
            ziggLagLastDirectionRef.current = 0
            ziggLagLastZ1Ref.current = null
            ziggLagLastZ2Ref.current = null
        }
    }, [extraIndicators.ziggLag, extraIndicatorParams.ziggLag?.depth, extraIndicatorParams.ziggLag?.deviation, extraIndicatorParams.ziggLag?.backstep, extraIndicatorParams.ziggLag?.lineWidth, prices, infoDisplay])
    
    // FlatBoll 绘制填充区域的 overlay 函数
    const drawFlatBollFillOverlays = (dataList) => {
        if (!chart.current || !dataList || dataList.length < 2) {
            console.log('drawFlatBollFillOverlays: early return', { chart: !!chart.current, dataLength: dataList?.length })
            return
        }
        
        console.log('drawFlatBollFillOverlays: starting', { dataLength: dataList.length })
        
        // 清除之前的 overlay
        flatBollOverlaysRef.current.forEach(id => {
            try {
                chart.current.removeOverlay(id)
            } catch (e) {
                // ignore
            }
        })
        flatBollOverlaysRef.current = []
        
        const params = extraIndicatorParams.flatBoll ?? EXTRA_INDICATOR_DEFAULT_PARAMS.flatBoll
        const bbLength = params?.bbLength ?? 20
        const bbMult = params?.bbMult ?? 2.0
        const thresholdType = params?.thresholdType ?? 'Ticks'
        const thresholdWidth = params?.thresholdWidth ?? 0.055
        const thresholdWidthPercent = params?.thresholdWidthPercent ?? 1.0
        
        // 计算 FlatBoll 数据
        const flatBollData = calculateFlatBoll(dataList, {
            bbLength,
            bbSrc: 'close',
            bbMult,
            thresholdType,
            thresholdWidth,
            thresholdWidthPercent,
            addATRFilter: false,
            atrLength: 14,
            atrSmoothing: 'RMA',
            atrThreshold: 25
        })
        
        console.log('drawFlatBollFillOverlays: flatBollData', { length: flatBollData.length, sample: flatBollData.slice(0, 5) })
        
        if (flatBollData.length === 0) {
            console.log('drawFlatBollFillOverlays: no flatBollData')
            return
        }
        
        // 计算整个图表的最低价格和最高价格
        const minPrice = Math.min(...dataList.map(c => c.low))
        const maxPrice = Math.max(...dataList.map(c => c.high))
        
        console.log('drawFlatBollFillOverlays: price range', { minPrice, maxPrice })
        
        // 为每个K线绘制填充区域（根据isFlat状态改变颜色）
        for (let i = 0; i < dataList.length; i++) {
            const candle = dataList[i]
            const flatBoll = flatBollData[i]
            
            if (!flatBoll || flatBoll.isFlat == null) {
                continue
            }
            
            const isFlat = flatBoll.isFlat ?? false
            const bgColor = isFlat 
                ? 'rgba(33, 250, 243, 0.15)'  // lime背景（平坦），15%透明度
                : 'rgba(250, 5, 10, 0.15)'    // red背景（不平坦），15%透明度
            
            // 计算当前K线的起始和结束时间戳（每个K线占一个时间段）
            const startTimestamp = candle.timestamp
            const endTimestamp = i < dataList.length - 1 
                ? dataList[i + 1].timestamp 
                : candle.timestamp + (candle.timestamp - (i > 0 ? dataList[i - 1].timestamp : candle.timestamp))
            
            try {
                // rect overlay 只需要两个点（左上角和右下角）
                const fillId = chart.current.createOverlay({
                    name: 'rect',
                    styles: {
                        polygon: {
                            color: bgColor,
                            style: 'fill'
                        }
                    },
                    points: [
                        { timestamp: startTimestamp, value: maxPrice },
                        { timestamp: endTimestamp, value: minPrice }
                    ]
                })
                if (fillId) {
                    flatBollOverlaysRef.current.push(fillId)
                    if (i < 5) {
                        console.log('drawFlatBollFillOverlays: overlay created', { i, fillId, isFlat, bgColor })
                    }
                }
            } catch (e) {
                console.error('Failed to create FlatBoll fill:', e)
            }
        }
    }
    
    // FlatBoll 指标控制（使用指标系统绘制布林带，overlay 绘制填充区域）
    useEffect(() => {
        if (!chart.current) return
        
        console.log('FlatBoll useEffect triggered', { 
            enabled: extraIndicators.flatBoll, 
            pricesLength: prices?.length, 
            infoDisplay 
        })
        
        if (extraIndicators.flatBoll) {
            // 创建或更新 FlatBoll 指标（叠加在主图上）
            const params = extraIndicatorParams.flatBoll ?? EXTRA_INDICATOR_DEFAULT_PARAMS.flatBoll
            const bbLength = params?.bbLength ?? 20
            const bbMult = params?.bbMult ?? 2.0
            
            if (!flatBollIndicatorIdRef.current) {
                try {
                    const indicatorId = chart.current.createIndicator('FlatBoll', false, { 
                        id: 'candle_pane',
                        calcParams: [bbLength, bbMult]
                    })
                    if (indicatorId) {
                        flatBollIndicatorIdRef.current = indicatorId
                    }
                } catch (e) {
                    console.warn('Failed to create FlatBoll indicator:', e)
                }
            } else {
                // 更新参数
                try {
                    chart.current.overrideIndicator(flatBollIndicatorIdRef.current, {
                        calcParams: [bbLength, bbMult]
                    })
                } catch (e) {
                    console.warn('Failed to update FlatBoll indicator:', e)
                }
            }
            
            // 绘制填充区域 overlay（只在非回测模式时绘制）
            if (prices && prices.length > 0 && infoDisplay) {
                console.log('FlatBoll: calling drawFlatBollFillOverlays')
                drawFlatBollFillOverlays(prices)
            } else {
                console.log('FlatBoll: skipping drawFlatBollFillOverlays', { 
                    pricesLength: prices?.length, 
                    infoDisplay 
                })
            }
        } else {
            // 移除 FlatBoll 指标和 overlay
            if (flatBollIndicatorIdRef.current) {
                try {
                    chart.current.removeIndicator(flatBollIndicatorIdRef.current)
                    flatBollIndicatorIdRef.current = null
                } catch (e) {
                    console.warn('Failed to remove FlatBoll indicator:', e)
                }
            }
            // 清除 overlay
            flatBollOverlaysRef.current.forEach(id => {
                try {
                    chart.current?.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            flatBollOverlaysRef.current = []
        }
    }, [extraIndicators.flatBoll, extraIndicatorParams.flatBoll?.bbLength, extraIndicatorParams.flatBoll?.bbMult, extraIndicatorParams.flatBoll?.thresholdWidth, extraIndicatorParams.flatBoll?.thresholdWidthPercent, prices, infoDisplay])
    
    // ZLEMA overlay 引用（用于填充和标记）
    const zlemaOverlaysRef = useRef([])
    
    // 计算 ZLEMA 数据（用于绘制 overlay）
    const calculateZLEMAForOverlays = (dataList, length = 70, mult = 1.2) => {
        if (!dataList || dataList.length === 0) return { zlemaList: [], volatilityList: [], trendList: [] }
        
        const zlemaList = []
        const volatilityList = []
        const trendList = []
        const lag = Math.floor((length - 1) / 2)
        const emaAlpha = 2 / (length + 1)
        const atrAlpha = 1 / length
        const atrWindow = Math.max(length * 3, length)
        const atrQueue = []

        let ema = null
        let atr = null
        let prevClose = dataList[0]?.close ?? 0

        for (let i = 0; i < dataList.length; i++) {
            const candle = dataList[i]
            const src = candle.close
            const lagSrc = i >= lag ? dataList[i - lag].close : src
            const adjusted = src + (src - lagSrc)
            ema = ema == null ? adjusted : (emaAlpha * adjusted + (1 - emaAlpha) * ema)
            zlemaList.push(ema)

            const high = candle.high
            const low = candle.low
            const tr = Math.max(
                high - low,
                Math.abs(high - prevClose),
                Math.abs(low - prevClose)
            )
            prevClose = candle.close
            atr = atr == null ? tr : (atr + (tr - atr) * atrAlpha)
            atrQueue.push(atr)
            if (atrQueue.length > atrWindow) {
                atrQueue.shift()
            }
            const volatility = Math.max(...atrQueue) * mult
            volatilityList.push(volatility)

            let trend = trendList[i - 1] ?? 0
            if (candle.close > ema + volatility) {
                trend = 1
            } else if (candle.close < ema - volatility) {
                trend = -1
            }
            trendList.push(trend)
        }
        return { zlemaList, volatilityList, trendList }
    }
    
    // 计算趋势变化状态（从策略中提取）
    const calculateTrendStateForIndicator = (data, swingLen = 2, boxPct = 0.18, minBoxBars = 3) => {
        const states = []
        let trend = 'unknown'
        let boxTop = null
        let boxBot = null
        let boxStartIndex = null
        let boxCount = 0
        
        let ph = null
        let pl = null
        let phBarIndex = null
        let plBarIndex = null
        
        // 计算 pivot high/low
        const pivotMap = new Map()
        for (let i = swingLen; i < data.length - swingLen; i++) {
            const candle = data[i]
            let isPivotHigh = true
            let isPivotLow = true
            
            for (let j = i - swingLen; j <= i + swingLen; j++) {
                if (j !== i && data[j].high >= candle.high) {
                    isPivotHigh = false
                    break
                }
            }
            
            for (let j = i - swingLen; j <= i + swingLen; j++) {
                if (j !== i && data[j].low <= candle.low) {
                    isPivotLow = false
                    break
                }
            }
            
            if (isPivotHigh || isPivotLow) {
                pivotMap.set(i, {
                    pivotHigh: isPivotHigh ? candle.high : null,
                    pivotLow: isPivotLow ? candle.low : null
                })
            }
        }
        
        for (let i = 0; i < data.length; i++) {
            const candle = data[i]
            const pivot = pivotMap.get(i)
            
            if (pivot) {
                if (pivot.pivotHigh != null) {
                    ph = pivot.pivotHigh
                    phBarIndex = i
                }
                if (pivot.pivotLow != null) {
                    pl = pivot.pivotLow
                    plBarIndex = i
                }
            }
            
            if (trend === 'unknown' && pl != null) {
                trend = 'Down'
            }
            
            const inPotentialBox = ph != null && pl != null && phBarIndex != null && plBarIndex != null && phBarIndex > plBarIndex
            const rangeHeight = inPotentialBox ? ph - pl : null
            const rangePct = inPotentialBox && pl > 0 ? rangeHeight / pl : null
            
            if (inPotentialBox && rangePct != null && rangePct <= boxPct) {
                if (candle.high <= ph && candle.low >= pl) {
                    boxCount += 1
                } else {
                    boxCount = 0
                }
            } else {
                boxCount = 0
            }
            
            const isBox = boxCount >= minBoxBars
            
            if (isBox && (boxTop == null || ph !== boxTop || pl !== boxBot)) {
                trend = 'Consolidation'
                boxTop = ph
                boxBot = pl
                boxStartIndex = plBarIndex
            }
            
            let breakoutUp = false
            let breakoutDn = false
            let backInBox = false
            
            if (boxTop != null && boxBot != null && i > 0) {
                const prevCandle = data[i - 1]
                breakoutUp = candle.close > boxTop && prevCandle.close <= boxTop
                breakoutDn = candle.close < boxBot && prevCandle.close >= boxBot
                backInBox = (candle.close < boxTop && prevCandle.close >= boxTop) || 
                            (candle.close > boxBot && prevCandle.close <= boxBot)
            }
            
            if (breakoutUp) {
                trend = 'UP'
                boxTop = null
                boxBot = null
                boxStartIndex = null
                boxCount = 0
            } else if (breakoutDn) {
                trend = 'DOWN'
                boxTop = null
                boxBot = null
                boxStartIndex = null
                boxCount = 0
            } else if (backInBox && boxTop != null) {
                trend = 'Return to Consolidation'
            }
            
            states.push({
                trend,
                boxTop,
                boxBot,
                boxStartIndex,
                ph,
                pl,
                phBarIndex,
                plBarIndex,
                isBox,
                breakoutUp,
                breakoutDn,
                backInBox
            })
        }
        
        return states
    }
    
    // HULL 辅助函数
    const sqr = (x) => x * x
    const dist2 = (a, b) => {
        if (!a || !b || typeof a.x !== 'number' || typeof a.y !== 'number' || typeof b.x !== 'number' || typeof b.y !== 'number') {
            return 1 // 返回默认值避免除零错误
        }
        return sqr(a.x - b.x) + sqr(a.y - b.y)
    }
    const getVerticalIntersectionPoint = (pa, va, wa) => {
        if (!pa || !va || !wa || typeof pa.x !== 'number' || typeof pa.y !== 'number' || 
            typeof va.x !== 'number' || typeof va.y !== 'number' || 
            typeof wa.x !== 'number' || typeof wa.y !== 'number') {
            return { x: pa?.x ?? 0, y: pa?.y ?? 0 }
        }
        let l2 = dist2(va, wa)
        if (l2 === 0) {
            return { x: va.x, y: va.y }
        }
        let t = ((pa.x - va.x) * (wa.x - va.x) + (pa.y - va.y) * (wa.y - va.y)) / l2
        t = Math.max(0.0, Math.min(1.0, t))
        return { x: va.x + t * (wa.x - va.x), y: va.y + t * (wa.y - va.y) }
    }
    
    // 绘制 HULL overlay
    const drawHullOverlays = (dataList, shouldResetState = false, lineWidthParam = null) => {
        if (!chart.current || !dataList || dataList.length < 2) {
            console.warn('drawHullOverlays: invalid input', { hasChart: !!chart.current, dataLength: dataList?.length })
            return
        }
        
        // 获取线宽参数（优先使用传入的参数，否则从状态中读取）
        const lineWidth = lineWidthParam ?? (extraIndicatorParams.hull?.lineWidth ?? EXTRA_INDICATOR_DEFAULT_PARAMS.hull?.lineWidth ?? 3)
        
        // 先保存当前的 overlay ID，清除时不影响后续绘制
        const oldOverlayIds = [...hullOverlaysRef.current]
        hullOverlaysRef.current = []
        
        // 清除之前的 overlay
        oldOverlayIds.forEach(id => {
            try {
                chart.current.removeOverlay(id)
            } catch (e) {
                // ignore
            }
        })
        
        const pfClose = dataList.map(c => c.close)
        const pfHigh = dataList.map(c => c.high)
        const pfLow = dataList.map(c => c.low)
        const DataLen = dataList.length
        
        // 只在需要时重置状态（例如切换标的或首次加载）
        let mode, lastHighLine, lastLowLine, stopIndex
        if (shouldResetState) {
            // 完全重置：从头开始绘制
            mode = 1
            lastHighLine = []
            lastLowLine = []
            stopIndex = 0
        } else {
            // 参数变化时（如调整线宽）：使用保存的最终状态，但需要重新绘制整个序列
            // 我们需要使用最终状态，但在绘制时会从头开始累积，这样能保持绘制逻辑的一致性
            mode = hullModeRef.current || 1
            lastHighLine = hullLastHighLineRef.current || []
            lastLowLine = hullLastLowLineRef.current || []
            stopIndex = hullStopIndexRef.current || 0
            
            // 关键：即使使用旧状态，我们也要从头开始绘制，因为绘制是累积的过程
            // 但我们可以从 stopIndex 开始，因为之前的已经确定了
            // 实际上，为了保持绘制逻辑的完整性，我们应该从头开始，但使用当前的 mode 作为起始模式
            // 简化：参数变化时也从头开始绘制，只保持初始 mode
            const savedMode = mode
            mode = 1  // 从头开始
            lastHighLine = []
            lastLowLine = []
            stopIndex = 0
            // 注意：这里不使用 savedMode，因为我们希望重新计算整个序列，这样绘制才会完整
        }
        
        for (let j = 0; j < DataLen; j++) {
            const index = j
            if (index > 0) {
                if (mode === 1) {
                    // 高点模式
                    if (lastHighLine && lastHighLine.length >= 2 && lastHighLine[0] && lastHighLine[1]) {
                        const intersection = getVerticalIntersectionPoint(
                            { x: index, y: pfClose[index] },
                            lastHighLine[0],
                            lastHighLine[1]
                        )
                        if (pfClose[index] > intersection.y) {
                            stopIndex = index - 1
                            mode = 2
                        }
                    }
                } else if (mode === 2) {
                    // 低点模式
                    if (lastLowLine && lastLowLine.length >= 2 && lastLowLine[0] && lastLowLine[1]) {
                        const intersection = getVerticalIntersectionPoint(
                            { x: index, y: pfClose[index] },
                            lastLowLine[0],
                            lastLowLine[1]
                        )
                        if (pfClose[index] < intersection.y) {
                            stopIndex = index - 1
                            mode = 1
                        }
                    }
                }
                
                if (mode === 1) {
                    // 绘制高点 hull
                    const highPoints = [{ x: stopIndex, y: -1000 }]
                    for (let i = stopIndex; i <= index; i++) {
                        highPoints.push({ x: i, y: pfHigh[i] })
                    }
                    highPoints.push({ x: index, y: -1000 })
                    let highHullPoints = QuickHull.quickHull(highPoints.map(c => [c.x, c.y]))
                    highHullPoints = highHullPoints.filter(p => p.y !== -1000)
                    
                    highHullPoints.sort((a, b) => {
                        if (a.x === b.x) {
                            return a.y - b.y
                        }
                        return a.x - b.x
                    })
                    
                    for (let i = 0; i < highHullPoints.length - 1; i++) {
                        const hpoint = highHullPoints[i]
                        const next = i + 1
                        const npoint = highHullPoints[next]
                        if (!hpoint || !npoint || npoint.y === -1000 || hpoint.y === -1000) continue
                        if (typeof hpoint.x !== 'number' || typeof hpoint.y !== 'number' || 
                            typeof npoint.x !== 'number' || typeof npoint.y !== 'number') continue
                        if (hpoint.x < 0 || hpoint.x >= dataList.length || npoint.x < 0 || npoint.x >= dataList.length) continue
                        
                        try {
                            const lineId = chart.current.createOverlay({
                                name: 'segment',
                                points: [
                                    { timestamp: dataList[hpoint.x].timestamp, value: hpoint.y },
                                    { timestamp: dataList[npoint.x].timestamp, value: npoint.y }
                                ],
                                styles: {
                                    line: {
                                        color: 'green',
                                        size: lineWidth
                                    }
                                },
                                lock: false,
                                mode: 'strong_magnet'
                            })
                            if (lineId) hullOverlaysRef.current.push(lineId)
                        } catch (e) {
                            console.warn('Failed to create hull high line:', e)
                        }
                    }
                    // 确保 lastHighLine 包含有效的两个点
                    if (highHullPoints.length >= 2) {
                        const lastTwo = highHullPoints.slice(-2)
                        if (lastTwo[0] && lastTwo[1] && 
                            typeof lastTwo[0].x === 'number' && typeof lastTwo[0].y === 'number' &&
                            typeof lastTwo[1].x === 'number' && typeof lastTwo[1].y === 'number') {
                            lastHighLine = lastTwo
                        }
                    }
                } else {
                    // 绘制低点 hull
                    const lowPoints = [{ x: stopIndex, y: 100000 }]
                    for (let i = stopIndex; i <= index; i++) {
                        lowPoints.push({ x: i, y: pfLow[i] })
                    }
                    lowPoints.push({ x: index, y: 100000 })
                    let lowHullPoints = QuickHull.quickHull(lowPoints.map(c => [c.x, c.y]))
                    lowHullPoints = lowHullPoints.filter(p => p.y !== 100000)
                    
                    lowHullPoints.sort((a, b) => {
                        if (a.x === b.x) {
                            return a.y - b.y
                        }
                        return a.x - b.x
                    })
                    
                    for (let i = 0; i < lowHullPoints.length - 1; i++) {
                        const hpoint = lowHullPoints[i]
                        const next = i + 1
                        const npoint = lowHullPoints[next]
                        if (!hpoint || !npoint || npoint.y === 100000 || hpoint.y === 100000) continue
                        if (typeof hpoint.x !== 'number' || typeof hpoint.y !== 'number' || 
                            typeof npoint.x !== 'number' || typeof npoint.y !== 'number') continue
                        if (hpoint.x < 0 || hpoint.x >= dataList.length || npoint.x < 0 || npoint.x >= dataList.length) continue
                        
                        try {
                            const lineId = chart.current.createOverlay({
                                name: 'segment',
                                points: [
                                    { timestamp: dataList[hpoint.x].timestamp, value: hpoint.y },
                                    { timestamp: dataList[npoint.x].timestamp, value: npoint.y }
                                ],
                                styles: {
                                    line: {
                                        color: 'red',
                                        size: lineWidth
                                    }
                                },
                                lock: false,
                                mode: 'strong_magnet'
                            })
                            if (lineId) hullOverlaysRef.current.push(lineId)
                        } catch (e) {
                            console.warn('Failed to create hull low line:', e)
                        }
                    }
                    // 确保 lastLowLine 包含有效的两个点
                    if (lowHullPoints.length >= 2) {
                        const lastTwo = lowHullPoints.slice(-2)
                        if (lastTwo[0] && lastTwo[1] && 
                            typeof lastTwo[0].x === 'number' && typeof lastTwo[0].y === 'number' &&
                            typeof lastTwo[1].x === 'number' && typeof lastTwo[1].y === 'number') {
                            lastLowLine = lastTwo
                        }
                    }
                }
            }
        }
        
        // 更新 ref 状态
        hullModeRef.current = mode
        hullLastHighLineRef.current = lastHighLine
        hullLastLowLineRef.current = lastLowLine
        hullStopIndexRef.current = stopIndex
    }
    
    // 绘制趋势变化 overlay
    const drawTrendChangeOverlays = (dataList) => {
        if (!chart.current || !dataList || dataList.length < 2) return
        
        // 清除之前的 overlay
        trendChangeOverlaysRef.current.forEach(id => {
            try {
                chart.current.removeOverlay(id)
            } catch (e) {
                // ignore
            }
        })
        trendChangeOverlaysRef.current = []
        
        const params = extraIndicatorParams.trendChange ?? EXTRA_INDICATOR_DEFAULT_PARAMS.trendChange
        const swingLen = params.swingLen ?? 2
        const boxPct = (params.boxPct ?? 18.0) / 100 // 转换为小数
        const minBoxBars = params.minBoxBars ?? 3
        
        const states = calculateTrendStateForIndicator(dataList, swingLen, boxPct, minBoxBars)
        if (states.length === 0) return
        
        const latestState = states[states.length - 1]
        const latestCandle = dataList[dataList.length - 1]
        
        if (!latestState || !latestCandle) return
        
        // 绘制 Box
        if (latestState.trend === 'Consolidation' && latestState.boxTop != null && latestState.boxBot != null && latestState.boxStartIndex != null) {
            const boxStartCandle = dataList[latestState.boxStartIndex]
            if (boxStartCandle) {
                try {
                    const boxId = chart.current.createOverlay({
                        name: 'rect',
                        styles: {
                            polygon: {
                                color: 'rgba(255, 255, 0, 0.1)',
                                style: 'fill'
                            },
                            line: {
                                color: 'orange',
                                size: 1,
                                style: 'solid'
                            }
                        },
                        points: [
                            { timestamp: boxStartCandle.timestamp, value: latestState.boxTop },
                            { timestamp: latestCandle.timestamp, value: latestState.boxBot }
                        ]
                    })
                    if (boxId) trendChangeOverlaysRef.current.push(boxId)
                } catch (e) {
                    console.warn('Failed to create consolidation box:', e)
                }
            }
        }
        
        // 绘制 Box Top/Bottom 线
        if (latestState.boxTop != null) {
            try {
                const lineId = chart.current.createOverlay({
                    name: 'straightLine',
                    points: [
                        { timestamp: dataList[0].timestamp, value: latestState.boxTop },
                        { timestamp: latestCandle.timestamp, value: latestState.boxTop }
                    ],
                    styles: {
                        line: {
                            color: 'orange',
                            size: 1,
                            style: 'dashed'
                        }
                    }
                })
                if (lineId) trendChangeOverlaysRef.current.push(lineId)
            } catch (e) {
                console.warn('Failed to create box top line:', e)
            }
        }
        if (latestState.boxBot != null) {
            try {
                const lineId = chart.current.createOverlay({
                    name: 'straightLine',
                    points: [
                        { timestamp: dataList[0].timestamp, value: latestState.boxBot },
                        { timestamp: latestCandle.timestamp, value: latestState.boxBot }
                    ],
                    styles: {
                        line: {
                            color: 'orange',
                            size: 1,
                            style: 'dashed'
                        }
                    }
                })
                if (lineId) trendChangeOverlaysRef.current.push(lineId)
            } catch (e) {
                console.warn('Failed to create box bottom line:', e)
            }
        }
        
        // 绘制 Label
        let labelBgColor = 'gray'
        if (latestState.trend === 'DOWN') labelBgColor = 'red'
        else if (latestState.trend === 'Consolidation') labelBgColor = 'yellow'
        else if (latestState.trend === 'UP') labelBgColor = 'green'
        
        try {
            const labelId = chart.current.createOverlay({
                name: 'simpleAnnotation',
                extendData: `${latestState.trend}\nPrice: ${latestCandle.close.toFixed(2)}`,
                points: [{ timestamp: latestCandle.timestamp, value: latestCandle.high }],
                styles: {
                    annotation: {
                        text: {
                            color: 'black',
                            backgroundColor: labelBgColor,
                            borderColor: labelBgColor
                        },
                        offset: [0, -20]
                    }
                }
            })
            if (labelId) trendChangeOverlaysRef.current.push(labelId)
        } catch (e) {
            console.warn('Failed to create trend label:', e)
        }
    }
    
    // 绘制 ZLEMA overlay（填充和标记）
    const drawZLEMAOverlays = (dataList) => {
        if (!chart.current || !dataList || dataList.length < 2) return
        
        // 清除之前的 overlay
        zlemaOverlaysRef.current.forEach(id => {
            try {
                chart.current.removeOverlay(id)
            } catch (e) {
                // ignore
            }
        })
        zlemaOverlaysRef.current = []
        
        const params = extraIndicatorParams.zlema ?? EXTRA_INDICATOR_DEFAULT_PARAMS.zlema
        const { zlemaList, volatilityList, trendList } = calculateZLEMAForOverlays(dataList, params.length, params.multiplier)
        
        // 计算 ATR
        const calcATR = (data, period = 5) => {
            if (data.length < period) return 0
            let sum = 0
            for (let i = data.length - period; i < data.length; i++) {
                if (i === 0) continue
                const prev = data[i - 1]
                const curr = data[i]
                const tr = Math.max(
                    curr.high - curr.low,
                    Math.abs(curr.high - prev.close),
                    Math.abs(curr.low - prev.close)
                )
                sum += tr
            }
            return sum / period
        }
        
        const atr5 = calcATR(dataList, 5)
        
        // 绘制填充和标记
        for (let i = 1; i < dataList.length; i++) {
            const candle = dataList[i]
            const prevCandle = dataList[i - 1]
            const trend = trendList[i] ?? 0
            const zlema = zlemaList[i]
            const volatility = volatilityList[i]
            
            if (zlema == null || volatility == null) continue
            
            // 填充区域
            if (trend === 1) {
                try {
                    const fillId = chart.current.createOverlay({
                        name: 'rect',
                        styles: {
                            polygon: {
                                color: 'rgba(0, 255, 187, 0.2)',
                                style: 'fill'
                            }
                        },
                        points: [
                            { timestamp: prevCandle.timestamp, value: zlema },
                            { timestamp: candle.timestamp, value: zlema },
                            { timestamp: candle.timestamp, value: zlema - volatility },
                            { timestamp: prevCandle.timestamp, value: zlema - volatility }
                        ]
                    })
                    if (fillId) zlemaOverlaysRef.current.push(fillId)
                } catch (e) {
                    console.warn('Failed to create bullish fill:', e)
                }
            } else if (trend === -1) {
                try {
                    const fillId = chart.current.createOverlay({
                        name: 'rect',
                        styles: {
                            polygon: {
                                color: 'rgba(255, 17, 0, 0.2)',
                                style: 'fill'
                            }
                        },
                        points: [
                            { timestamp: prevCandle.timestamp, value: zlema },
                            { timestamp: candle.timestamp, value: zlema },
                            { timestamp: candle.timestamp, value: zlema + volatility },
                            { timestamp: prevCandle.timestamp, value: zlema + volatility }
                        ]
                    })
                    if (fillId) zlemaOverlaysRef.current.push(fillId)
                } catch (e) {
                    console.warn('Failed to create bearish fill:', e)
                }
            }
            
            // 趋势标记
            if (i >= 4) {
                const zlemaUpTrend = zlemaList[i] > zlemaList[i - 1] &&
                                    zlemaList[i - 1] > zlemaList[i - 2] &&
                                    zlemaList[i - 2] > zlemaList[i - 3] &&
                                    zlemaList[i - 3] > zlemaList[i - 4]
                
                if (zlemaUpTrend) {
                    try {
                        const triangleId = chart.current.createOverlay({
                            name: 'simpleAnnotation',
                            extendData: '▲',
                            points: [{ timestamp: candle.timestamp, value: candle.low - atr5 * 0.5 }],
                            styles: {
                                annotation: {
                                    text: {
                                        color: '#00ff00',
                                        backgroundColor: 'rgba(0, 255, 0, 0.2)',
                                        borderColor: '#00ff00'
                                    }
                                }
                            }
                        })
                        if (triangleId) zlemaOverlaysRef.current.push(triangleId)
                    } catch (e) {
                        console.warn('Failed to create uptrend marker:', e)
                    }
                }
                
                const zlemaDownTrend = zlemaList[i] < zlemaList[i - 1] &&
                                      zlemaList[i - 1] < zlemaList[i - 2] &&
                                      zlemaList[i - 2] < zlemaList[i - 3] &&
                                      zlemaList[i - 3] < zlemaList[i - 4]
                
                if (zlemaDownTrend) {
                    try {
                        const triangleId = chart.current.createOverlay({
                            name: 'simpleAnnotation',
                            extendData: '▼',
                            points: [{ timestamp: candle.timestamp, value: candle.high + atr5 * 0.5 }],
                            styles: {
                                annotation: {
                                    text: {
                                        color: '#ff0000',
                                        backgroundColor: 'rgba(255, 0, 0, 0.2)',
                                        borderColor: '#ff0000'
                                    }
                                }
                            }
                        })
                        if (triangleId) zlemaOverlaysRef.current.push(triangleId)
                    } catch (e) {
                        console.warn('Failed to create downtrend marker:', e)
                    }
                }
            }
        }
    }
    
    // ZLEMA 指标控制（主图）
    useEffect(() => {
        if (!chart.current) return
        
        if (extraIndicators.zlema) {
            // 创建 ZLEMA 指标（叠加在主图上）
            if (!zlemaIndicatorIdRef.current) {
                try {
                    const params = extraIndicatorParams.zlema ?? EXTRA_INDICATOR_DEFAULT_PARAMS.zlema
                    const indicatorId = chart.current.createIndicator('ZLEMA', false, { 
                        id: 'candle_pane',
                        calcParams: [params.length, params.multiplier]
                    })
                    if (indicatorId) {
                        zlemaIndicatorIdRef.current = indicatorId
                    }
                } catch (e) {
                    console.warn('Failed to create ZLEMA indicator:', e)
                }
            } else {
                // 更新参数
                try {
                    const params = extraIndicatorParams.zlema ?? EXTRA_INDICATOR_DEFAULT_PARAMS.zlema
                    chart.current.overrideIndicator(zlemaIndicatorIdRef.current, {
                        calcParams: [params.length, params.multiplier]
                    })
                } catch (e) {
                    console.warn('Failed to update ZLEMA indicator:', e)
                }
            }
            
            // 绘制 overlay（填充和标记）
            if (prices && prices.length > 0 && infoDisplay) {
                drawZLEMAOverlays(prices)
                if (extraIndicators.trendChange) {
                    drawTrendChangeOverlays(prices)
                }
                if (extraIndicators.hull) {
                    const currentLineWidth = extraIndicatorParams.hull?.lineWidth ?? EXTRA_INDICATOR_DEFAULT_PARAMS.hull?.lineWidth ?? 3
                    drawHullOverlays(prices, true, currentLineWidth)  // 数据更新时重置状态
                }
            }
        } else {
            // 移除 ZLEMA 指标和 overlay
            if (zlemaIndicatorIdRef.current) {
                try {
                    chart.current.removeIndicator(zlemaIndicatorIdRef.current)
                    zlemaIndicatorIdRef.current = null
                } catch (e) {
                    console.warn('Failed to remove ZLEMA indicator:', e)
                }
            }
            // 清除 overlay
            zlemaOverlaysRef.current.forEach(id => {
                try {
                    chart.current.removeOverlay(id)
                } catch (e) {
                    // ignore
                }
            })
            zlemaOverlaysRef.current = []
        }
    }, [extraIndicators.zlema, extraIndicatorParams.zlema?.length, extraIndicatorParams.zlema?.multiplier, prices])
    
    // 预测 API 调用函数
    const callPredictAPI = async (klineData) => {
        if (!klineData || klineData.length === 0) {
            return null
        }
        
        const params = extraIndicatorParams.predict ?? EXTRA_INDICATOR_DEFAULT_PARAMS.predict
        const apiBaseUrl = params.apiBaseUrl ?? 'http://192.168.2.65:5000'
        const predLen = params.predLen ?? 10
        const T = params.T ?? 0.9
        const topP = params.topP ?? 0.9
        const sampleCount = params.sampleCount ?? 3
        
        try {
            setIsPredicting(true)
            const response = await axios.post(`${apiBaseUrl}/predict`, {
                kline_data: klineData.map(candle => ({
                    open: candle.open,
                    high: candle.high,
                    low: candle.low,
                    close: candle.close,
                    volume: candle.volume || 0,
                    amount: candle.amount || 0,
                    timestamp: candle.timestamp ? new Date(candle.timestamp).toISOString().replace('T', ' ').substring(0, 19) : undefined
                })),
                pred_len: predLen,
                T: T,
                top_p: topP,
                sample_count: sampleCount
            }, {
                timeout: 60000
            })
            
            if (response.data.success) {
                return response.data.predictions
            } else {
                console.error('预测失败:', response.data.error)
                return null
            }
        } catch (error) {
            console.error('预测 API 调用失败:', error)
            return null
        } finally {
            setIsPredicting(false)
        }
    }
    
    // 初始化 Predict 子图
    const initPredictChart = () => {
        if (!extraIndicators.predict) {
            // 如果禁用了，清理子图
            if (predictChartRef.current) {
                try {
                    dispose('predict-k-line')
                } catch (e) {
                    console.warn('Failed to dispose predict chart:', e)
                }
                predictChartRef.current = null
            }
            // 清理 overlay
            if (predictOverlayRef.current) {
                try {
                    chart.current?.removeOverlay(predictOverlayRef.current)
                } catch (e) {
                    // ignore
                }
                predictOverlayRef.current = null
            }
            setPredictData([])
            return
        }
        
        // 创建子图
        if (!predictChartRef.current) {
            try {
                predictChartRef.current = init('predict-k-line')
                predictChartRef.current.setStyles(Style)
                // 子图不需要任何指标，只要K线图
            } catch (e) {
                console.error('Failed to init predict chart:', e)
                predictChartRef.current = null
            }
        }
    }
    
    // 更新 Predict 子图数据
    const updatePredictChart = async (dataList) => {
        if (!extraIndicators.predict || !predictChartRef.current || !dataList || dataList.length === 0) {
            return
        }
        
        // 过滤掉 ghost bar（开盘价等于收盘价且成交量为 0 的 K 线）
        const actualData = dataList.filter(candle => {
            // ghost bar 的特征：open === close && volume === 0
            return !(candle.open === candle.close && candle.volume === 0)
        })
        
        if (actualData.length === 0) {
            return
        }
        
        // 调用预测 API（使用过滤后的实际数据）
        const predictions = await callPredictAPI(actualData)
        if (!predictions || predictions.length === 0) {
            return
        }
        
        // 合并原始数据和预测数据（子图显示时也过滤 ghost bar）
        const lastCandle = actualData[actualData.length - 1]
        const lastTimestamp = lastCandle.timestamp
        
        // 计算时间间隔（假设是日线）
        const timeInterval = actualData.length > 1 
            ? (actualData[actualData.length - 1].timestamp - actualData[actualData.length - 2].timestamp)
            : 24 * 60 * 60 * 1000 // 默认24小时
        
        // 将预测数据的 timestamp 字符串转换为时间戳
        const combinedData = [
            ...actualData,
            ...predictions.map((pred, index) => {
                // 解析 timestamp 字符串为时间戳
                let predTimestamp
                if (pred.timestamp) {
                    // 格式: "2025-11-05 16:00:00"
                    predTimestamp = new Date(pred.timestamp.replace(' ', 'T')).getTime()
                } else {
                    // 如果没有 timestamp，使用计算的时间间隔
                    predTimestamp = lastTimestamp + timeInterval * (index + 1)
                }
                
                return {
                    open: pred.open,
                    high: pred.high,
                    low: pred.low,
                    close: pred.close,
                    volume: pred.volume || 0,
                    timestamp: predTimestamp,
                    time: predTimestamp
                }
            })
        ]
        
        setPredictData(combinedData)
        
        // 更新子图数据（只更新子图，不影响主图）
        try {
            predictChartRef.current.applyNewData(combinedData)
            
            // 在子图上绘制红色半透明 overlay 覆盖预测部分
            if (predictOverlayRef.current) {
                try {
                    predictChartRef.current.removeOverlay(predictOverlayRef.current)
                } catch (e) {
                    // ignore
                }
            }
            
            // 计算预测部分的起始和结束时间戳
            const predStartTimestamp = predictions.length > 0 
                ? (predictions[0].timestamp ? new Date(predictions[0].timestamp.replace(' ', 'T')).getTime() : lastTimestamp + timeInterval)
                : lastTimestamp + timeInterval
            const predEndTimestamp = predictions.length > 0 
                ? (predictions[predictions.length - 1].timestamp ? new Date(predictions[predictions.length - 1].timestamp.replace(' ', 'T')).getTime() : lastTimestamp + timeInterval * predictions.length)
                : lastTimestamp + timeInterval * predictions.length
            
            // 计算整个图表的最低价格和最高价格
            const minPrice = Math.min(...combinedData.map(c => c.low))
            const maxPrice = Math.max(...combinedData.map(c => c.high))
            
            // 创建红色半透明 overlay
            predictOverlayRef.current = predictChartRef.current.createOverlay({
                name: 'rect',
                styles: {
                    polygon: {
                        color: 'rgba(255, 0, 0, 0.2)',  // 红色半透明
                        style: 'fill'
                    }
                },
                points: [
                    { timestamp: predStartTimestamp, value: maxPrice },
                    { timestamp: predEndTimestamp, value: minPrice }
                ]
            })
        } catch (e) {
            console.error('Failed to update predict chart:', e)
        }
    }
    
    const toggleExtraIndicator = (key) => {
        setExtraIndicators(prev => ({
            ...prev,
            [key]: !prev[key]
        }))
    }
    const updateExtraIndicatorParam = (indicatorKey, paramKey, value) => {
        setExtraIndicatorParams(prev => ({
            ...prev,
            [indicatorKey]: {
                ...(prev[indicatorKey] ?? EXTRA_INDICATOR_DEFAULT_PARAMS[indicatorKey] ?? {}),
                [paramKey]: value
            }
        }))
    }
    const visibleStrategies = useMemo(() => {
        return userStrategies.filter(strat => {
            // 游戏策略始终显示
            if (strat.name === '游戏') {
                return true
            }
            // 如果没有Tools组件，不显示
            if (!strat.Tools) {
                return false
            }
            // 检查策略可见性设置
            const flag = strategyVisibility[strat.name]
            return flag !== false
        })
    }, [strategyVisibility, userStrategies])
    const toggleStrategyVisibility = (name) => {
        if (name === '游戏') {
            return
        }
        setStrategyVisibility(prev => {
            const next = { ...prev }
            next[name] = !(prev[name] !== false)
            return next
        })
    }
    useEffect(() => {
        if (!strategy || !visibleStrategies.some(item => item.name === strategy.name)) {
            const fallback = visibleStrategies.find(item => item.name === '游戏') || visibleStrategies[0] || userStrategies[0]
            if (fallback && strategy?.name !== fallback.name) {
                setStrategy(fallback)
                if (typeof window !== 'undefined') {
                    localStorage.setItem('strategy', fallback.name)
                }
            }
        }
    }, [visibleStrategies, strategy])
    useEffect(() => {
        if(trader) {
            trader.price = price;
        }
    }, [price, trader]);
    useEffect(() => {
        if(search){
            let stocks = []
            for(let stock of stocksData){
                if(stock.name.includes(search.toLowerCase()) || stock.code.includes(search.toLowerCase()) || stock.pinyin.includes(search.toLowerCase())){
                    stocks.push(stock)
                }
            }
            setSearchedStocks(stocks)
            // console.log(stocks)
        } else {
            setSearchedStocks([])
        }
    }, [search]);
    useEffect(() => {
        chart.current = init('indicator-k-line')
        window.klchart = chart.current
        registerIndicator(KD)
        registerIndicator(BOLLMA)
        registerIndicator(HIGH_SLOPE)
        registerIndicator(SMART_BOTTOM)
        registerIndicator(BUGKDJ)
        registerIndicator(ANGLE)
        registerIndicator(BOLLBREAKOUT)
        registerIndicator(ZLEMA)
        registerIndicator(ZIGLAG)
        registerIndicator(FlatBoll)
        paneId.current = chart.current.createIndicator('KD', false)
        chart.current.createIndicator('VOL', false)
        chart.current.createIndicator('HIGH_SLOPE', false)
        chart.current.createIndicator('BOLL_BREAKOUT', false)
        // chart.current.createIndicator('SLOPE_SIGNAL', false)
        // chart.current.createIndicator('BUGKDJ', false)
        // chart.current.createIndicator('ANGLE', false)
        // chart.current.createIndicator('RSI', false)
        chart.current.setStyles(Style)
        chart.current.createIndicator("BOLLMA", false, { id: 'candle_pane' })
        
        // ZLEMA 指标在主图上的引用，根据 toggle 控制
        window.zlemaIndicatorIdRef = { current: null }
        chart.current.subscribeAction('onCandleBarClick', function (param) {
            // console.log(param)
        });
        trader = new Trader({
            setPrice,
            setCash,
            setPosition,
            setProfit, // 新增：传递 setProfit 方法
            setCumulativeReturn // 新增：传递 setCumulativeReturn 方法
        });
        datasource.init(function(res){
            if(res){
                console.log(res)
            }
            setSourceReady(true)
            console.log("datasource ready")
        })

        socket.on("lines", (lines) => {
            for(var line of lastLines){
                chart.current.removeOverlay(line)
            }
            console.log("lines", lines)
            for(var line of lines){
                (function(_line){
                    _line.onClick = function(){
                    console.log("click", _line);
                    setSelectedLine(_line);
                }})(line)
                chart.current.createOverlay(line)
            }
            lastLines = lines;
        })
        return () => {
            dispose('indicator-k-line')
            if (predictChartRef.current) {
                try {
                    dispose('predict-k-line')
                } catch (e) {
                    // ignore
                }
            }
        }
    }, [])
    
    // 初始化/清理 Predict 子图
    useEffect(() => {
        initPredictChart()
        
        return () => {
            if (predictChartRef.current) {
                try {
                    dispose('predict-k-line')
                } catch (e) {
                    // ignore
                }
                predictChartRef.current = null
            }
        }
    }, [extraIndicators.predict])
    
    // 当参数变化时，如果子图已启用，重新更新子图
    useEffect(() => {
        if (extraIndicators.predict && prices && prices.length > 0 && predictChartRef.current) {
            updatePredictChart(prices)
        }
    }, [extraIndicatorParams.predict?.predLen, extraIndicatorParams.predict?.T, extraIndicatorParams.predict?.topP, extraIndicatorParams.predict?.sampleCount])
    useEffect(() => {
        return () => {
            removeBreakoutOverlays()
        }
    }, [])
    useEffect(() => {
        if(code && chart.current && sourceReady){
            (async () => {
                for(var line of lastLines){
                    chart.current.removeOverlay(line)
                }
                removeBreakoutOverlays()
                const data = await Datasource.getKlines(code, period, endTime, lines)

                console.log(data)
                var last = data[data.length - 1];
                if(last && window && window.location.href.indexOf("boll")>=0){
                    data.push({
                        high: last.close,
                        low: last.close,
                        open: last.close,
                        close: last.close,
                        timestamp: last.timestamp + 1000 * 3600*24,
                        time: last.time + 1000 * 3600*24
                    })
                }


                chart.current.applyNewData(data)
                setPrices(data.slice())
                for(let strategy of indicatorStrategies){
                    if(strategy.reset){
                        strategy.reset(chart.current)
                    }
                    strategy.update(chart.current, data)
                }
                updateBreakoutOverlays(chart.current, data)
                if (extraIndicators.trendChange) {
                    drawTrendChangeOverlays(data)
                }
                
                // 更新 Predict 子图
                if (extraIndicators.predict) {
                    await updatePredictChart(data)
                }
            })();
            console.log("going to join", code + ":" + period);
            socket.emit("join", code + ":" + period)

        }

    }, [code, chart, period, sourceReady, extraIndicators.predict]);
    useEffect(() => {
        setSearch('')
    }, [menuShow])
    const hideStrategyInfo = () => {
        setStrategyInfoShow(false)
    };
    const step = (previewCount) => {
        let step = strategyStep
        if (step >= prices.length) {
            return;
        }
        setStrategyStep(step + 1)
        console.log(step)
        if(step==0){
            chart.current.applyNewData([prices[0]])
        }else {
            chart.current.updateData(prices[step])
        }
        strategy.update(chart.current, prices.slice(0, step + 1))
    }
    const clear = () => {
        setStrategyStep(0)
        chart.current.applyNewData(prices)
    }

    const periods = ["1d", "1w", "15m", "30m", "1h", "2h", "1M"]
    return (
        <div style={{width: '100%', height: '100vh', display: 'flex', flexDirection: 'row', overflow: 'hidden', position: 'relative'}} className={"container-fluid"}>
            <div className={"row"} style={{height: '100vh', width: '100%', margin: 0, flex: 1, display: 'flex', position: 'relative'}}>
                <div style={{height: '100vh', display: 'flex', flexDirection: 'column', minHeight: 0, padding: 0, position: 'relative'}} className={"col-lg-2 col-xxl-2 d-lg-block d-xxl-block  " + (menuShow?"d-sm-block d-md-block d-block ": "d-none")}>
                    <div style={{flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', minHeight: 0, padding: '12px', position: 'relative'}}>
                        {window&&window.location&&window.location.href.indexOf("search")>=0&&<div style={{display: 'flex', flexDirection: 'row', alignItems:'center'}}>
                            <input type="text" value={search} style={{flex:1}} placeholder={"标的代码/首字母"} onChange={(e) => {
                                setSearch(e.target.value)
                            }}/>
                            <FontAwesomeIcon icon={faXmark} onClick={()=>{
                                setSearch("")
                            }}/>
                        </div>}
                        {searchedStocks.length > 0 && <div style={{marginTop: 10}}>
                            {searchedStocks.map((stock, index) => {
                                    return <div key={index} style={{cursor: 'pointer', marginTop: 5}} onClick={() => {
                                        setCode(stock.code)
                                        setStockName(stock.name)
                                        setMenuShow(false)
                                    }}>
                                        <div style={{color: code == stock.code ? "red" : "white"}}>{stock.name}</div>
                                        <div style={{fontSize: 12, color: 'rgba(255,255,255,0.5)'}}>{stock.code}</div>
                                    </div>
                                }
                            )}
                        </div>}
                        <div style={{marginTop: 20}}>
                            <div>收藏</div>
                            <div style={{marginTop: 10}}>
                                {favs.map((fav, index) => {
                                    return <div key={index} style={{cursor: 'pointer', marginTop: 5}} onClick={() => {
                                        setCode(fav.code)
                                        setStockName(fav.name)
                                        setMenuShow(false)
                                    }}>
                                        <div style={{color: code == fav.code ? "red" : "white"}}>{fav.name}</div>
                                        <div style={{fontSize: 12, color: 'rgba(255,255,255,0.5)'}}>{fav.code}</div>
                                    </div>
                                })}
                            </div>
                        </div>
                    </div>
                    <div style={{paddingTop: 12, paddingBottom: 100, paddingLeft: 12, paddingRight: 12, borderTop: '1px solid rgba(255,255,255,0.1)', flexShrink: 0, marginTop: 'auto'}}>
                        <EquityChart history={equityHistory} />
                    </div>
                </div>
                <div className="k-line-chart-container col-lg-10 col-xxl-10 col-12" style={{paddingTop: 15, position: 'relative', display: 'flex', flexDirection: 'row', overflow: 'hidden'}}>
                    <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>
                    <div style={{position: 'absolute', top: 5, right: 10, zIndex: 30, display: 'flex', flexDirection: 'column', alignItems: 'flex-end'}}>
                        <button
                            style={{
                                padding: '4px 10px',
                                borderRadius: 12,
                                border: '1px solid rgba(255,255,255,0.3)',
                                background: 'rgba(0,0,0,0.35)',
                                color: '#ffffff',
                                cursor: 'pointer'
                            }}
                            onClick={() => setSettingsVisible(prev => !prev)}
                        >设置</button>
                        {settingsVisible && (
                            <div
                                onClick={e => e.stopPropagation()}
                                style={{
                                    marginTop: 8,
                                    background: 'rgba(20,24,34,0.96)',
                                    border: '1px solid rgba(255,255,255,0.12)',
                                    borderRadius: 14,
                                    padding: 18,
                                    minWidth: 320,
                                    maxHeight: 360,
                                    overflowY: 'auto',
                                    boxShadow: '0 16px 36px rgba(0,0,0,0.45)'
                                }}
                            >
                                <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10}}>
                                    <span style={{color: '#ffffff', fontSize: 14, whiteSpace: 'nowrap'}}>策略显示</span>
                                    <button
                                        style={{
                                            background: 'transparent',
                                            border: 'none',
                                            color: 'rgba(255,255,255,0.6)',
                                            cursor: 'pointer',
                                            fontSize: 16,
                                            lineHeight: '16px'
                                        }}
                                        onClick={() => setSettingsVisible(false)}
                                    >✕</button>
                                </div>
                                <div style={{display: 'flex', flexDirection: 'column', gap: 14}}>
                                    {userStrategies.map(opt => {
                                        const checked = opt.name === '游戏' ? true : strategyVisibility[opt.name] !== false
                                        const CommentComponent = opt.comment
                                        return (
                                            <div key={opt.name} style={{display: 'flex', flexDirection: 'column', gap: 6, padding: '12px 14px', borderRadius: 12, background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.08)'}}>
                                                <div style={{display: 'flex', alignItems: 'center', width: '100%'}}>
                                                    <div style={{color: '#ffffff', fontSize: 13, width: 200, flex: 1, whiteSpace: 'nowrap', paddingRight: 12}}>{opt.name}</div>
                                                    <input
                                                        type="checkbox"
                                                        checked={checked}
                                                        style={{width:50}}
                                                        disabled={opt.name === '游戏'}
                                                        onChange={() => toggleStrategyVisibility(opt.name)}
                                                    />
                                                </div>
                                                {CommentComponent && (
                                                    <div style={{color: 'rgba(255,255,255,0.55)', fontSize: 12, lineHeight: '18px'}}>
                                                        <CommentComponent />
                                                    </div>
                                                )}
                                            </div>
                                        )
                                    })}
                                </div>
                                <div style={{marginTop: 20, paddingTop: 20, borderTop: '1px solid rgba(255,255,255,0.12)'}}>
                                    <div style={{color: '#ffffff', fontSize: 14, marginBottom: 10}}>指标显示（首页）</div>
                                    <div style={{display: 'flex', flexDirection: 'column', gap: 10}}>
                                        {EXTRA_INDICATOR_OPTIONS.map(option => {
                                            const visible = extraIndicatorsVisibility[option.key] !== false
                                            return (
                                                <div key={option.key} style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', borderRadius: 8, background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.08)'}}>
                                                    <div style={{color: '#ffffff', fontSize: 13, flex: 1}}>{option.label}</div>
                                                    <input
                                                        type="checkbox"
                                                        checked={visible}
                                                        style={{width: 50}}
                                                        onChange={() => {
                                                            setExtraIndicatorsVisibility(prev => ({
                                                                ...prev,
                                                                [option.key]: !prev[option.key]
                                                            }))
                                                        }}
                                                    />
                                                </div>
                                            )
                                        })}
                                    </div>
                                </div>
                            </div>
                        )}
                    </div>
                    {infoDisplay && <div className="k-line-chart-title">{stockName}</div>}
                    {infoDisplay && <div className={"periods"} style={{display: 'flex', flexDirection: 'row'}}>
                        {periods.map((p, index) => {
                            return <div key={index} className={period == p ? "active" : ""} onClick={() => {
                                setPeriod(p)
                            }}>{p}</div>
                        })}
                    </div>}
                    <div style={{display: 'flex', flexDirection: 'column', marginTop: 10, marginBottom:10, fontSize:12}}
                         className={"col-lg-8 col-xxl-8 col-12"}>


                        <div style={{display: 'flex', flexDirection: 'row', alignItems: 'center'}}>
                            <div style={{cursor: 'pointer'}} onClick={() => {
                                chart.current.createOverlay({
                                    name: "straightLine",
                                    onDrawEnd: function (event) {
                                        var overlay = event.overlay;
                                        socket.emit("drawline", code, period, overlay);
                                    },
                                })
                            }}>画线
                            </div>
                            {selectedLine && <div style={{cursor: 'pointer', marginLeft: 15}} onClick={() => {
                                socket.emit("removeline", code, period, selectedLine.id)
                                chart.current.removeOverlay(selectedLine)
                                setSelectedLine(null)
                            }}>删除线
                            </div>}
                            {favs.filter(c => c.code == code).length == 0 &&
                                <div style={{cursor: 'pointer', marginLeft: 15}} onClick={() => {
                                    for (var fav of favs) {
                                        if (fav.code == code) {
                                            return;
                                        }
                                    }
                                    let newFavs = [...favs]
                                    newFavs.push({code, name: stockName})
                                    setFavs(newFavs)
                                    localStorage.setItem("favs", JSON.stringify(newFavs))
                                }}>收藏
                                </div>}


                            {favs.filter(c => c.code == code).length > 0 && defaultFavs.filter(c => c.code == code).length == 0 &&
                                <div style={{cursor: 'pointer', marginLeft: 15}} onClick={() => {
                                    let newFavs = [...favs]
                                    newFavs = newFavs.filter(c => c.code != code)
                                    setFavs(newFavs)
                                    localStorage.setItem("favs", JSON.stringify(newFavs))
                                }}>取消收藏</div>}
                            {trader &&
                                <div style={{
                                    color: 'white',
                                    marginLeft: 15
                                }}>模拟资产:{isNaN(Math.floor(cash + position)) ? 0 : Math.floor(cash + position)}</div>}
                            {trader && (
                                <div style={{ color: 'white', marginLeft: 15 }}>
                                    累计收益: {profit.toFixed(2)}
                                </div>
                            )}
                            {trader && (
                                <div style={{ color: 'white', marginLeft: 15 }}>
                                    累积收益率: {cumulativeReturn.toFixed(2)}%
                                </div>
                            )}
                        </div>
                        <div style={{display: 'flex', flexDirection: 'column', marginTop: 8, gap: 6}}>
                            {/* Extra Indicator 参数 */}
                            {EXTRA_INDICATOR_OPTIONS.map(option => {
                                // 如果设置中禁用了，则不显示 toggle 按钮
                                if (extraIndicatorsVisibility[option.key] === false) {
                                    return null
                                }
                                const active = !!extraIndicators[option.key]
                                const params = extraIndicatorParams[option.key] ?? EXTRA_INDICATOR_DEFAULT_PARAMS[option.key]
                                return (
                                    <div key={option.key} style={{display: 'flex', flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: 8}}>
                                        <button
                                            onClick={() => toggleExtraIndicator(option.key)}
                                            style={{
                                                padding: '4px 12px',
                                                borderRadius: 12,
                                                border: '1px solid',
                                                borderColor: active ? '#0dd697' : 'rgba(255,255,255,0.25)',
                                                backgroundColor: active ? 'rgba(13, 214, 151, 0.18)' : 'transparent',
                                                color: active ? '#0dd697' : '#ffffff',
                                                cursor: 'pointer',
                                                fontSize: 12,
                                                lineHeight: '16px'
                                            }}
                                        >
                                            {option.label}
                                        </button>
                                        <div style={{color: 'rgba(255,255,255,0.45)'}}>:</div>
                                        {option.params?.map(param => {
                                            const baseValue = params?.[param.key]
                                            const defaultValue = EXTRA_INDICATOR_DEFAULT_PARAMS[option.key]?.[param.key]
                                            const sliderValue = typeof baseValue === 'number' ? baseValue : (typeof defaultValue === 'number' ? defaultValue : param.min)
                                            const formattedValue = param.decimals != null ? Number(sliderValue.toFixed(param.decimals)).toString() : String(sliderValue)
                                            return (
                                                <div key={param.key} style={{display: 'flex', alignItems: 'center', gap: 6}}>
                                                    <div style={{color: 'rgba(255,255,255,0.65)', fontSize: 12}}>{param.label}</div>
                                                    <input
                                                        type="range"
                                                        min={param.min}
                                                        max={param.max}
                                                        step={param.step}
                                                        value={sliderValue}
                                                        disabled={!active}
                                                        onChange={e => updateExtraIndicatorParam(option.key, param.key, Number(e.target.value))}
                                                        style={{width: 150}}
                                                    />
                                                    <div style={{minWidth: 32, textAlign: 'right', color: active ? (param.color ?? '#0dd697') : 'rgba(255,255,255,0.3)', fontSize: 12}}>
                                                        {formattedValue}
                                                    </div>
                                                </div>
                                            )
                                        })}
                                    </div>
                                )
                            })}
                        </div>
                    </div>
                    <div id="indicator-k-line" className="k-line-chart" style={{flex: 1}}/>
                </div>
                {/* Predict 子图 */}
                {extraIndicators.predict && (
                    <div style={{width: '400px', height: '100%', display: 'flex', flexDirection: 'column', borderLeft: '1px solid rgba(255,255,255,0.1)', paddingLeft: 10, minWidth: 0}}>
                        <div style={{padding: '10px 0', color: '#ffffff', fontSize: 14}}>
                            Predict {isPredicting && <span style={{fontSize: 12, color: 'rgba(255,255,255,0.5)'}}>(预测中...)</span>}
                        </div>
                        <div id="predict-k-line" className="k-line-chart" style={{flex: 1, minHeight: 0}}/>
                    </div>
                )}
                <div style={{display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: 50, marginTop:30}}>
                    {strategyInfoShow&&<><div>策略</div>
                    <div style={{width: 80, marginLeft:15}}>
                        <select
                            value={Math.max(0, visibleStrategies.findIndex(item => item.name === strategy?.name))}
                            onChange={e => {
                                const idx = Number(e.target.value)
                                const selectedStrategy = visibleStrategies[idx]
                                if (selectedStrategy) {
                                    setStrategy(selectedStrategy)
                                    if (typeof window !== 'undefined') {
                                        localStorage.setItem("strategy", selectedStrategy.name)
                                    }
                                }
                            }}
                        >
                            {visibleStrategies.map((s, index) => {
                                return <option key={s.name} value={index}>{s.name}</option>
                            })}
                        </select>
                    </div></>}
                    {/*<div style={{marginLeft: 10}}>*/}
                    {/*    <div style={{marginBottom: 10}}>说明</div>*/}
                    {/*    <strategy.comment></strategy.comment>*/}
                    {/*</div>*/}

                    <div style={{marginLeft: 15, flex: 1}}>
                        {/*<div style={{marginBottom: 10}}>工具</div>*/}
                        <strategy.Tools getKlines={datasource.getKlines} indicatorStrategies={indicatorStrategies}
                                        setInfoDisplay={setInfoDisplay} code={code} chart={chart} trader={trader}
                                        setPrice={setPrice} prices={prices} stocks={stocksData}
                                        dates={tradingDates} hideStrategyInfo={hideStrategyInfo}
                                        onDataApplied={handleExternalDataApplied}
                                        onEquityHistoryChange={setEquityHistory}></strategy.Tools>
                    </div>
                    <FontAwesomeIcon className={"d-xxl-none d-lg-none d-block"} icon={faMagnifyingGlass}  style={{color: 'white', marginLeft: 15, marginRight: 20}} onClick={()=>{
                        setMenuShow(true)
                    }}/>
                </div>
            </div>
            {/*<div style={{padding: 15, display: 'flex', flexDirection: 'column'}}*/}
            {/*     className={"col-lg-2 col-xxl-2 col-12"}>*/}

            {/*    <div style={{display: 'flex'}}>*/}
            {/*        <button onClick={() => {*/}
            {/*            step(1)*/}
            {/*        }}>步进调试*/}
            {/*        </button>*/}

            {/*        <button style={{marginLeft: 5}} onClick={() => {*/}
            {/*            clear()*/}
            {/*        }}>清空*/}
            {/*        </button>*/}
            {/*        <button style={{marginLeft: 5}}>运行</button>*/}
            {/*    </div>*/}
            {/*</div>*/}
        </div>
    </div>

    )
}
